package edu.mtu.citizenscience.cs.sdk.login;

import com.androidquery.AQuery;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;

import edu.mtu.citizenscience.cs.sdk.ResponseHandler;
import edu.mtu.citizenscience.cs.sdk.ServerRequest;
import edu.mtu.citizenscience.cs.sdk.json.Request;
import edu.mtu.citizenscience.cs.sdk.json.Response;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
/**
 * @author Corbin Uselton <corbinu@cwuselto@mtu.edu>
 * @copyright (c) 2012 MichiganTech <http://www.mtu.edu>
 * @since 1.0
 * 
 * Manages user login for CitSci uploads
 */
public class CitsciLogin {
	// login storage file location
	private static final String LOGIN_STORAGE_FILE = "LoginStorageFile";
	// callback to activity that is logging the user in
	private CitsciLoginCallback callback;
	// shared preferences storage
	private SharedPreferences storage;
	// android-query library
	private AQuery aq;
	
	/**
	 * @since 1.0
	 * 
	 * Format for the response payload from the login request
	 */
	public static class LoginResponse {
		// the user's remote key
		private String key;
		
		/**
		 * Create the login response
		 * 
		 * @param   void
		 * @return	void
		 */
		public LoginResponse() { }

		/**
		 * Get the user's remote key from the response
		 * 
		 * @param   void
		 * @return	String - remote key
		 */
		public String getKey() 
		{
			return this.key;
		}

		/**
		 * Set the user's remote key from the response
		 * 
		 * @param   key - String - remote key
		 * @return	void
		 */
		public void setKey(String key) 
		{
			this.key = key;
		}
	}
	
	/**
	 * @since 1.0
	 * 
	 * Format for the login request 
	 */
	public static class LoginRequest {
		// the users username or email address
		private String username;
		// the users password
		private String password;
		
		/**
		 * Create the login request
		 * 
		 * @param   username - String - the users username or email
		 * @param	password - String - the users password
		 * @return	void
		 */
		public LoginRequest(String username, String password) 
		{
			this.username = username;
			this.password = password;
		}
		
		/**
		 * Get the users username
		 * 
		 * @param   void
		 * @return	String - users username
		 */
		public String getUsername() 
		{
			return username;
		}
		
		/**
		 * Set the users username
		 * 
		 * @param   username - String - users username
		 * @return	void
		 */
		public void setUsername(String username) 
		{
			this.username = username;
		}
		
		/**
		 * Get the users password
		 * 
		 * @param   void
		 * @return	String - users password
		 */
		public String getPassword() 
		{
			return password;
		}
		
		/**
		 * Set the users password
		 * 
		 * @param   password - String - users password
		 * @return	void
		 */
		public void setPassword(String password) 
		{
			this.password = password;
		}
	}
	
	/**
	 * @since 1.0
	 * 
	 * Handles response from the login request
	 */
	private class LoginResponseHandler extends ResponseHandler<LoginResponse> {
		// stores the user login
		private UserLogin login;
		
		/**
		 * Create the Login Response Handler
		 * 
		 * @param   username - String - the users username or email
		 * @return	void
		 */
		public LoginResponseHandler(String username)
		{
			// pass the response type
			super(LoginResponse.class);
			
			// create new login
			this.login = new UserLogin();
			// set the username
			this.login.setUsername(username);
		}

		/**
		 * Called on response from the server
		 * 
		 * @param   response - Response<LoginResponse> - the response from the server
		 * @return	void
		 */
		@Override
		public void response(Response<LoginResponse> response)
		{
			try 
			{
				// get the users remote key and store it in the login object
				this.login.setKey(response.getPayload().getKey());
				
				// store the new login
				storeLogin(this.login);

				// return success to the activity logging the user in with the username and key
				callback.loginSuccess(this.login);
			} 
			catch (JsonParseException e) 
			{
				// if there is  a problem parsing the response pass the error along to the activity
				callback.loginError(CitsciLoginError.INVALID_RESPONSE);
				e.printStackTrace();
			}
			catch (JsonMappingException e) 
			{
				// if there is  a problem parsing the response pass the error along to the activity
				callback.loginError(CitsciLoginError.INVALID_RESPONSE);
				e.printStackTrace();
			}
		}

		/**
		 * Called on an error on the server
		 * 
		 * @param   response - Response<LoginResponse> - the response from the server
		 * @return	void
		 */
		@Override
		public void error(Response<LoginResponse> response) 
		{
			// print out the developer error message
			Log.e("Login", response.getError().getDeveloperMessage());
			// pass the user error message onto the activity
			callback.loginErrorMessage(response.getError().getUserMessage());
		}

		/**
		 * Called on no response from the server
		 * 
		 * @param   void
		 * @return	void
		 */
		@Override
		public void noResponse() 
		{
			// pass the no connection error onto the activity
			callback.loginError(CitsciLoginError.NO_CONNECTION);
		}
	}
	
	/**
	 * Create the CitsciLogin object
	 * 
	 * @param   aq - AQuery - the managing Activity's AQuery instance
	 * @return	void
	 */
	public CitsciLogin(AQuery aq)
	{
		// save the aq instance
		this.aq = aq;
		
		// get the sharded preferances file in private mode to secure the username and remote key
		this.storage = this.aq.getContext().getSharedPreferences(LOGIN_STORAGE_FILE, Context.MODE_PRIVATE);
	}
	
	/**
	 * Get the stored user login
	 * 
	 * @param   void
	 * @return	UserLogin
	 */
	public UserLogin getLogin()
	{
		UserLogin login = new UserLogin();
		
		login.setUsername(storage.getString("username", ""));
		login.setKey(storage.getString("key", ""));
		
		return login;
	}
	
	/**
	 * Store a user login
	 * 
	 * @param   login - UserLogin - username and password
	 * @return	void
	 */
	public void storeLogin(UserLogin login)
	{
		// get the shared preferences editor
		SharedPreferences.Editor editor = this.storage.edit();
		
		// store the username and remote key
		editor.putString("username", login.getUsername());
		editor.putString("key", login.getKey());
		
		// commit the changes to the preferences file
		editor.commit();
	}
	
	/**
	 * Clear the stored a user login
	 * 
	 * @param   void
	 * @return	void
	 */
	public void clearLogin()
	{
		// store an empty user login
		this.storeLogin(new UserLogin());
	}
	
	/**
	 * Make login request to the server
	 * 
	 * @param   username - String - the users username or email
	 * @param	password - String - the users password
	 * @param	callback - CitsciLoginCallback - the callback for the login response
	 * @return	void
	 */
	public void doLogin(String username, String password, CitsciLoginCallback callback)
	{
		// save the callback object to respond to the activity once the user is logged in
		this.callback = callback;
		
		// create a new API request for the get_key action with a login request as the payload
		Request<LoginRequest> request = new Request<LoginRequest>("get_key", new LoginRequest(username, password));
		
		// do the request on the user resource with the login response handler on response
		ServerRequest.doRequest(this.aq, "users", request, new LoginResponseHandler(username));
	}

}
